﻿using System;
using System.Collections.Generic;
using System.Text;
/*
	Jun 2012: Added a property to track nullable columns
*/
namespace DbView
{
	public class FieldInfo : ICloneable
	{
		String name;
		String sqlType;
		String type;
		int length;
		int decimalPlaces = 0;
		bool isPKey = false;
		bool isFKey = false;
		bool isIndexed = false;
		bool isAuto = false;
		bool isUnicode = false;
		bool isCalc = false;
		bool allowsNulls = false;
		// need
		bool hasDefault = false;
		int autoSeed = 0;
		int autoInc = 0;
		// for use with both calc and default
		String constraintName = "";
		String constraintDefinition = "";

		public FieldInfo(String name, String sqlType, int len)
		{
			this.name = name;
			this.sqlType = sqlType;
			this.type = this.TypeFromSqlType(this.sqlType);
			this.length = len;
		}

		public object Clone()
		{
			FieldInfo clone = new FieldInfo(this.name, this.sqlType, this.length);
			clone.decimalPlaces = this.decimalPlaces;
			clone.IsPKey = this.IsPKey;
			clone.IsFKey = this.IsFKey;
			clone.IsIndexed = this.IsIndexed;
			clone.IsAuto = this.IsAuto;
			clone.IsCalc = this.isCalc;
			clone.AllowsNulls = this.AllowsNulls;
			clone.IsUnicode = this.IsUnicode;
			clone.HasDefault = this.HasDefault;
			clone.ConstraintName = this.ConstraintName;
			clone.ConstraintDefinition = this.ConstraintDefinition;

			return clone;
		}

		public String Name { get { return this.name; } set { this.name = value; } }
		public String SqlType { get { return this.sqlType; } set { this.sqlType = value; } }
		public String Type { get { return this.type; } set { this.type = value; } }
		public int Length { get { return this.length; } set { this.length = value; } }
		public int Dp { get { return this.decimalPlaces; } set { this.decimalPlaces = value; } }
		public bool IsPKey { get { return this.isPKey; } set { this.isPKey = value; } }
		public bool IsFKey { get { return this.isFKey; } set { this.isFKey = value; } }
		public bool IsIndexed { get { return this.isIndexed; } set { this.isIndexed = value; } }
		public bool IsAuto { get { return this.isAuto; } set { this.isAuto = value; } }
		public bool IsCalc { get { return this.isCalc; } set { this.isCalc = value; } }
		public bool AllowsNulls { get { return this.allowsNulls; } set { this.allowsNulls = value; } }
		public bool IsUnicode { get { return this.isUnicode; } set { this.isUnicode = value; } }
		// need 
		public bool HasDefault { get { return this.hasDefault; } set { this.hasDefault = value; } }
		public int AutoSeed
		{
			get
			{
				if (!this.isAuto) return 0;
				return (this.autoSeed == 0) ? 1 : this.autoSeed;
			}
			set { this.autoSeed = value; }
		}
		public int AutoInc
		{
			get
			{
				if (!this.isAuto) return 0;
				return (this.autoInc == 0) ? 1 : this.autoInc;
			}
			set { this.autoInc = value; }
		}
		public String ConstraintName { get { return this.constraintName; } set { this.constraintName = value; } }
		public String ConstraintDefinition { get { return this.constraintDefinition; } set { this.constraintDefinition = value; } }

		public String SafeName
		{
			get
			{
				// test whether already wrapped?
				if (this.name.IndexOf("[") != 0) return "[" + this.name + "]";
				if (this.name.IndexOf("]") != this.name.Length - 1) return "[" + this.name + "]";
				return this.name;
			}
		}

		public String MakeLiteral(String data)
		{
			if (data.Length == 0) return "NULL";
			if (this.Type == "D") return this.Quote(data);
			if (this.Type == "B") return (data.ToUpper() == "TRUE" ? "1" :
									 data.ToUpper() == "FALSE" ? "0" :
									 data.ToUpper() == "YES" ? "1" :
									 data.ToUpper() == "NO" ? "0" :
									 data.ToUpper() == "Y" ? "1" :
									 data.ToUpper() == "N" ? "0" : "0");
			if (this.Type == "C") return this.Quote(data);
			return data;
		}

		public int Match(FieldInfo[] searchFields)
		{
			// convert target to normalised value
			string target = this.SafeName.ToUpper();
			// extend to case and [] insensitive
			for (int idx = 0; idx < searchFields.Length; ++idx)
			{
				if (searchFields[idx].SafeName.ToUpper() == target)
					return idx; // bingo!
			}
			return -1; // aww!
		}

		private string Quote(string data)
		{
			/*
				AAA -> 'AAA' or N'AAA'
				'AAA' -> N'AAA' if unicode
				N'AAA' -> 'AAA' if not unicode 
			*/
			bool isUnicodeQuoted;
			if (!this.IsQuoted(data, out isUnicodeQuoted))
				return ((this.IsUnicode) ? "N" : "") + "'" + data.Replace("\'", "\'\'") + "'";
			if (isUnicodeQuoted && !this.IsUnicode) return data.Substring(1); // string unwanted N
			return ((this.IsUnicode && !isUnicodeQuoted) ? "N" : "") + data;
		}

		bool IsQuoted(String data, out bool isUnicodeQuoted)
		{
			isUnicodeQuoted = false;
			if (data.Length < 2) return false; // can't be
			if (data[data.Length - 1] != '\'') return false; // doesn't end in '
			if (data[0] == '\'') return true; // normal eg. 'text'
			if ((data[0] == 'N' || data[0] == 'n') && data[1] == '\'')
			{
				isUnicodeQuoted = true;
				return true;
			}
			return false;
		}

		// Now includes SQLite affinity although slightly modified in favour of SQL server
		// http://www.sqlite.org/datatype3.html
		private String TypeFromSqlType(String sqlType)
		{
			String upperSqlType = sqlType.ToUpper();
			return //(USqlType == "BIGINT") ? "J" :  // not sure. this is a bit SQL server specific
			       (upperSqlType.Contains("INT")) ? "I" :
				   (upperSqlType == "BIT") ? "B" :
				   (upperSqlType.Contains("FLOAT")) ? "N" :
				   (upperSqlType.Contains("REAL")) ? "N" :
				   (upperSqlType.Contains("DOUB")) ? "N" :
				   (upperSqlType == "DECIMAL") ? "N" :
				   (upperSqlType == "DATETIME") ? "D" :
										 "C";
		}
	}
}
